import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ServerThread implements Runnable {
	private Socket socket = null;
	private InputStream in;
	private OutputStream out;
	private ObjectInputStream objIn = null;
	private ObjectOutputStream objOut = null;
	private Message receivedMessage = null;
	private String dictionaryWord;
	private List<String> foundCharacters = new ArrayList<String>();
	private int attemptLeft = 0;
	private int scoreClient = 0;
	private final int numberOfDictionaryEntries = 25142;

	public ServerThread(Socket socket) {
		this.socket = socket;
	}

	public void run() {
		try {
			in = socket.getInputStream();
			out = socket.getOutputStream();
			objIn = new ObjectInputStream(in);
			objOut = new ObjectOutputStream(out);
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			while ((receivedMessage = (Message) objIn.readObject()).getType() != Message.STOP_GAME) {

				String receivedString = receivedMessage.getArg();
				
				if (receivedMessage.getType() == Message.START_GAME) 
					startGame();
				
				// response message
				Message responseMsg = checkAnswer(receivedString);
				isGameOver(responseMsg);
	
				responseMsg.setAttemptLeft(attemptLeft);
				sendMsg(responseMsg);
			}
			objIn.close();
			objOut.close();
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * function to call when the game is started
	 */
	private void startGame() {
		System.out.println("start game");
		try {
			selectRandomWordInDictionary();
			foundCharacters.clear();
			attemptLeft = dictionaryWord.length()+1;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	/**
	 * function to call to verify if the game is over or not
	 */
	private void isGameOver(Message responseMsg){
		if (attemptLeft == 0) {
			responseMsg.setType(Message.GAME_LOST);
			responseMsg.setArg(dictionaryWord);
			scoreClient = scoreClient - 1;
			responseMsg.setScore(scoreClient);
		}

		if (testIfGameWin()) {
			responseMsg.setType(Message.GAME_WON);
			scoreClient = scoreClient + 1;
			responseMsg.setScore(scoreClient);
		}
	}


	/**
	 * Function to check the answer
	 * 
	 * @param arg: letter or word send by the client
	 */
	private Message checkAnswer(String arg) {
		Message responseMsg = new Message();

		if (!arg.isEmpty()) {
			if (arg.length() > 1) 
				isTheDictionaryWord(responseMsg, arg);
			else 
				addNewLetterToFoundList(arg, responseMsg);
			
		} else {
			responseMsg.setArg(createResponseWithUnderscore(dictionaryWord,
					foundCharacters));
			responseMsg.setType(Message.SERVER_CLIENT);
		}

		return responseMsg;
	}
	
	/**
	 * function to check if the given string arg matches the dictionary word
	 * returns a win message or decrement the number of attempts
	 * @param responseMsg
	 * @param arg
	 */
	private void isTheDictionaryWord(Message responseMsg, String arg){
		if (arg.contentEquals(dictionaryWord)) {
			responseMsg.setType(Message.GAME_WON);// the given word perfectly matches
			responseMsg.setArg(dictionaryWord);
			scoreClient = scoreClient + 1;
			responseMsg.setScore(scoreClient);
			System.out.println("le mot est trouve");
		} else {
			responseMsg.setType(Message.SERVER_CLIENT);
			responseMsg.setArg(createResponseWithUnderscore(
					dictionaryWord, foundCharacters));
			attemptLeft--;
		}
	}
	
	/**
	 * If a new character of the dictionary word has been found, this function will add this new character
	 * to the list of found characters 
	 * @param arg
	 * @param responseMsg
	 */
	private void addNewLetterToFoundList(String arg, Message responseMsg){
		if (dictionaryWord.contains(arg)
				&& !foundCharacters.contains(arg)) {
			foundCharacters.add(arg);
		} else {
			attemptLeft--;
		}
		responseMsg.setArg(createResponseWithUnderscore(dictionaryWord,
				foundCharacters));
		responseMsg.setType(Message.SERVER_CLIENT);
	}

	/**
	 * This function creates a string for the hangman UI with underscores for
	 * unknown letters
	 * 
	 * @param str
	 *            the word from the dictionary
	 * @param list
	 *            the list of found characters
	 * @return
	 */
	private String createResponseWithUnderscore(String dicoWord,
			List<String> list) {
		StringBuilder response = new StringBuilder();
		int i = 0;
		while (i < dicoWord.length()) {
			response.append("_ ");
			i++;
		}
		for (String string : list) {
			int index = dicoWord.indexOf(string);

			while (index != -1) {
				response.replace(2 * index, 2 * index + 1, string);
				index = dicoWord.indexOf(string, index + 1);
			}
		}

		return response.toString();
	}

	/**
	 * return true if all the letters of the word has been found
	 * or false otherwise 
	 * @return
	 */
	private Boolean testIfGameWin() {
		for (int i = 0; i < dictionaryWord.length(); i++) {
			if (!foundCharacters.contains(String.valueOf(dictionaryWord
					.charAt(i)))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Function to send a message to the client attribute to the socketClient
	 * 
	 * @param msg : content the Message to send to the client
	 */
	private void sendMsg(Message msgToSend) {
		try {
			objOut.writeObject(msgToSend);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * returns a random word found in the given dictionary
	 * @throws IOException
	 */
	private synchronized void selectRandomWordInDictionary() throws IOException {
		BufferedReader inputStream = null;
		Random r = new Random();
		int entryNumber = r.nextInt(numberOfDictionaryEntries);
		try {
			try {
				inputStream = new BufferedReader(new FileReader("words.txt"));
			} catch (FileNotFoundException e) {
				System.out.println("file words.txt not found");
				e.printStackTrace();
			}

			String l = null;
			int i = 0;
			while ((l = inputStream.readLine()) != null && (i != entryNumber)) {
				i++;
			}
			dictionaryWord = l.toLowerCase();
		} finally {
			if (inputStream != null) {
				inputStream.close();
			}
		}

		System.out.println("message chose in dictionary " + dictionaryWord);
	}

}
